﻿using System;
using System.IO;
using System.Data;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Extensions.Options;
using Fast.Framework.Interfaces;
using Fast.Framework.Extensions;
using Fast.Framework.Models;
using System.Data.Common;

namespace Fast.Framework
{

    /// <summary>
    /// 数据库上下文实现类
    /// </summary>
    public class DbContext : IDbContext
    {

        /// <summary>
        /// 上下文Id
        /// </summary>
        public Guid ContextId { get; } = Guid.NewGuid();

        /// <summary>
        /// Ado
        /// </summary>
        public IAdo Ado { get; private set; }

        /// <summary>
        /// Aop
        /// </summary>
        public IAop Aop { get; }

        /// <summary>
        /// 数据库选项列表
        /// </summary>
        private readonly List<DbOptions> dbOptionsList;

        /// <summary>
        /// ados
        /// </summary>
        private readonly Dictionary<string, IAdo> ados;

        /// <summary>
        /// Sql表达式选项
        /// </summary>
        private readonly SqlExpressionOptions updateOptions;

        /// <summary>
        /// 构造方法
        /// </summary>
        /// <param name="options">数据库选项</param>
        public DbContext(DbOptions options)
        {
            Aop = new AopProvider();
            Ado = DynamicProxyFactory.Create<IAdo>().Create(new AdoProvider(options), (method, args) =>
            {
                Aop.DbLog?.Invoke(args[0] as string, args[1] as List<DbParameter>);
            });
            updateOptions = new SqlExpressionOptions()
            {
                IgnoreParameterExpression = true,
                DbType = Ado.DbOptions.DbType
            };
        }

        /// <summary>
        /// 构造方法
        /// </summary>
        /// <param name="options">数据库选项列表</param>
        public DbContext(List<DbOptions> options)
        {
            if (!options.Any())
            {
                throw new ArgumentNullException(nameof(options));
            }
            dbOptionsList = options;
            ados = new Dictionary<string, IAdo>();
            dbOptionsList = options;
            Aop = new AopProvider();
            var ado = DynamicProxyFactory.Create<IAdo>().Create(new AdoProvider(options[0]), (method, args) =>
            {
                Aop.DbLog?.Invoke(args[0] as string, args[1] as List<DbParameter>);
            });
            ados.Add(options[0].DbId, ado);
            Ado = ado;
            updateOptions = new SqlExpressionOptions()
            {
                IgnoreParameterExpression = true,
                DbType = Ado.DbOptions.DbType
            };
        }

        /// <summary>
        /// 构造方法
        /// </summary>
        /// <param name="options">数据库选项</param>
        public DbContext(IOptionsSnapshot<List<DbOptions>> options)
        {
            if (!options.Value.Any())
            {
                throw new ArgumentNullException(nameof(options));
            }
            dbOptionsList = options.Value;
            ados = new Dictionary<string, IAdo>();
            Aop = new AopProvider();
            var ado = DynamicProxyFactory.Create<IAdo>().Create(new AdoProvider(options.Value[0]), (method, args) =>
            {
                Aop.DbLog?.Invoke(args[0] as string, args[1] as List<DbParameter>);
            });
            ados.Add(options.Value[0].DbId, ado);
            this.Ado = ado;
            updateOptions = new SqlExpressionOptions()
            {
                IgnoreParameterExpression = true,
                DbType = Ado.DbOptions.DbType
            };
        }

        #region 增 删 改
        /// <summary>
        /// 插入
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="entity">实体</param>
        /// <returns></returns>
        public IInsert<T> Insert<T>(T entity) where T : class
        {
            if (entity == null)
            {
                throw new ArgumentNullException(nameof(entity));
            }
            var keyValues = entity.GetPropertyValue(true);
            return Insert<T>(keyValues);
        }

        /// <summary>
        /// 插入
        /// </summary>
        /// <param name="keyValues">键值</param>
        /// <returns></returns>
        public IInsert<T> Insert<T>(Dictionary<string, object> keyValues) where T : class
        {
            if (keyValues == null)
            {
                throw new ArgumentNullException(nameof(keyValues));
            }
            if (keyValues.Count == 0)
            {
                throw new Exception($"{nameof(keyValues)}个数为零");
            }
            var type = typeof(T);
            var insertBuilder = new InsertBuilder();
            insertBuilder.TableName = DbSymbolMapper.identify[Ado.DbOptions.DbType].Insert(1, type.GetTableName());
            insertBuilder.Columns = string.Join(",\r\n", keyValues.Keys.Select(s => DbSymbolMapper.identify[Ado.DbOptions.DbType].Insert(1, s)));
            insertBuilder.Value = string.Join(",\r\n", keyValues.Keys.Select(s => $"{DbSymbolMapper.parameter[Ado.DbOptions.DbType]}{s}"));
            insertBuilder.Parameters.Append(keyValues);
            return new InsertProvider<T>(Ado, insertBuilder);
        }

        /// <summary>
        /// 插入
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="entityList">实体列表</param>
        /// <returns></returns>
        public IInsert<T> Insert<T>(List<T> entityList) where T : class
        {
            if (entityList == null)
            {
                throw new ArgumentNullException(nameof(entityList));
            }
            var dictionaryList = entityList.GetPropertyValue(true);
            return Insert<T>(dictionaryList);
        }

        /// <summary>
        /// 插入
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="dictionaryList">字典列表</param>
        /// <returns></returns>
        public IInsert<T> Insert<T>(List<Dictionary<string, object>> dictionaryList) where T : class
        {
            if (dictionaryList == null)
            {
                throw new ArgumentNullException(nameof(dictionaryList));
            }
            if (!dictionaryList.Any())
            {
                throw new Exception($"{nameof(dictionaryList)}不存在任何元素");
            }
            var type = typeof(T);
            var insertBuilder = new InsertBuilder();
            insertBuilder.IsBatch = true;
            insertBuilder.TableName = DbSymbolMapper.identify[Ado.DbOptions.DbType].Insert(1, type.GetTableName());
            insertBuilder.Columns = string.Join(",\r\n", dictionaryList[0].Keys.Select(s => DbSymbolMapper.identify[Ado.DbOptions.DbType].Insert(1, s)));
            insertBuilder.Value = string.Join(",\r\n", dictionaryList[0].Keys.Select(s => $"{DbSymbolMapper.parameter[Ado.DbOptions.DbType]}{s}"));
            insertBuilder.ParametersList.Append(dictionaryList);
            return new InsertProvider<T>(Ado, insertBuilder);
        }

        /// <summary>
        /// 删除
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public IDelete<T> Delete<T>() where T : class
        {
            var deleteBuilder = new DeleteBuilder();
            var type = typeof(T);
            deleteBuilder.TableName = DbSymbolMapper.identify[Ado.DbOptions.DbType].Insert(1, type.GetTableName());
            return new DeleteProvider<T>(Ado, deleteBuilder);
        }

        /// <summary>
        /// 删除
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="obj">对象(这是一个虚拟对象主要是为了匿名类型可以使用Where方法)</param>
        /// <returns></returns>
        public IDelete<T> Delete<T>(T obj) where T : class
        {
            if (obj == null)
            {
                throw new ArgumentNullException(nameof(obj));
            }
            var deleteBuilder = new DeleteBuilder();
            var type = typeof(T);
            deleteBuilder.TableName = DbSymbolMapper.identify[Ado.DbOptions.DbType].Insert(1, type.GetTableName());
            return new DeleteProvider<T>(Ado, deleteBuilder);
        }

        /// <summary>
        /// 更新
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public IUpdate<T> Update<T>() where T : class
        {
            var type = typeof(T);
            var updateBuilder = new UpdateBuilder();
            updateBuilder.TableName = DbSymbolMapper.identify[Ado.DbOptions.DbType].Insert(1, type.GetTableName());
            return new UpdateProvider<T>(Ado, updateBuilder);
        }

        /// <summary>
        /// 更新
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="entity">实体</param>
        /// <returns></returns>
        public IUpdate<T> Update<T>(T entity) where T : class
        {
            if (entity == null)
            {
                throw new ArgumentNullException(nameof(entity));
            }
            var keyValues = entity.GetPropertyValue(true);
            return Update<T>(keyValues);
        }

        /// <summary>
        /// 更新
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="keyValues">键值</param>
        /// <returns></returns>
        public IUpdate<T> Update<T>(Dictionary<string, object> keyValues) where T : class
        {
            if (keyValues == null)
            {
                throw new ArgumentNullException(nameof(keyValues));
            }
            if (keyValues.Count == 0)
            {
                throw new Exception($"{nameof(keyValues)}个数为零");
            }
            var type = typeof(T);
            var updateBuilder = new UpdateBuilder();
            updateBuilder.TableName = DbSymbolMapper.identify[Ado.DbOptions.DbType].Insert(1, type.GetTableName());
            updateBuilder.Set = string.Join(",", keyValues.Select(s => $"{DbSymbolMapper.identify[Ado.DbOptions.DbType].Insert(1, s.Key)} = {DbSymbolMapper.parameter[Ado.DbOptions.DbType]}{s.Key}"));
            updateBuilder.Parameters.Append(keyValues);
            return new UpdateProvider<T>(Ado, updateBuilder);
        }

        /// <summary>
        /// 更新
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="entityList">实体列表</param>
        /// <returns></returns>
        public IUpdate<T> Update<T>(List<T> entityList) where T : class
        {
            if (entityList == null)
            {
                throw new ArgumentNullException(nameof(entityList));
            }
            var dictionaryList = entityList.GetPropertyValue();
            return Update<T>(dictionaryList);
        }

        /// <summary>
        /// 更新
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="dictionaryList">字典列表</param>
        /// <returns></returns>
        public IUpdate<T> Update<T>(List<Dictionary<string, object>> dictionaryList) where T : class
        {
            if (dictionaryList == null)
            {
                throw new ArgumentNullException(nameof(dictionaryList));
            }
            if (!dictionaryList.Any())
            {
                throw new Exception($"{nameof(dictionaryList)}不存在任何元素");
            }
            var type = typeof(T);
            var updateBuilder = new UpdateBuilder();
            updateBuilder.IsBatch = true;
            updateBuilder.TableName = DbSymbolMapper.identify[Ado.DbOptions.DbType].Insert(1, type.GetTableName());
            updateBuilder.ParametersList.Append(dictionaryList);

            var keys = type.GetPrimaryKey();
            updateBuilder.Set = string.Join(",", dictionaryList[0].Where(w => !keys.Exists(e => e == w.Key)).Select(s => $"{DbSymbolMapper.identify[Ado.DbOptions.DbType].Insert(1, s.Key)} = {DbSymbolMapper.parameter[Ado.DbOptions.DbType]}{s.Key}"));
            if (keys.Count > 0)
            {
                foreach (var key in keys)
                {
                    updateBuilder.Where.Add($"{DbSymbolMapper.identify[Ado.DbOptions.DbType].Insert(1, key)} = {DbSymbolMapper.parameter[Ado.DbOptions.DbType]}{key}");//主键
                }
            }
            return new UpdateProvider<T>(Ado, updateBuilder);
        }

        /// <summary>
        /// 更新
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="expression">表达式</param>
        /// <returns></returns>
        public IUpdate<T> Update<T>(Expression<Func<T, T>> expression) where T : class
        {
            var type = typeof(T);
            var updateBuilder = new UpdateBuilder();
            updateBuilder.TableName = DbSymbolMapper.identify[Ado.DbOptions.DbType].Insert(1, type.GetTableName());
            var sqlInfo = expression.ResolveSql(updateOptions);
            updateBuilder.Set = string.Join(",", sqlInfo.NewAssignMapper);
            updateBuilder.Parameters.Append(sqlInfo.SqlParameters);
            return new UpdateProvider<T>(Ado, updateBuilder);
        }
        #endregion

        #region 大数据
        /// <summary>
        /// 大数据
        /// </summary>
        /// <returns></returns>
        public IBigData BigData()
        {
            return new BigDataProvider(Ado);
        }
        #endregion

        #region 查询
        /// <summary>
        /// 查询
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public IQuery<T> Query<T>() where T : class, new()
        {
            var type = typeof(T);
            var queryBuilder = new QueryBuilder();
            queryBuilder.TableName = type.Equals(typeof(object)) ? "" : $"{DbSymbolMapper.identify[Ado.DbOptions.DbType].Insert(1, typeof(T).GetTableName())} p0";
            queryBuilder.Columns = "*";
            return new QueryProvider<T>(Ado, queryBuilder);
        }

        /// <summary>
        /// 子查询
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="query">查询对象</param>
        /// <returns></returns>
        public T SubQuery<T>(IQuery query)
        {
            return default;
        }

        /// <summary>
        /// 联合
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="querys">查询</param>
        /// <returns></returns>
        public IQuery<T> Union<T>(params IQuery<T>[] querys) where T : class
        {
            return Union(querys.ToList());
        }

        /// <summary>
        /// 联合
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="querys">查询</param>
        /// <returns></returns>
        /// <exception cref="ArgumentNullException"></exception>
        public IQuery<T> Union<T>(List<IQuery<T>> querys) where T : class
        {
            if (querys == null || querys.Count == 0)
            {
                throw new ArgumentNullException(nameof(querys));
            }
            var queryBuilder = new QueryBuilder();
            queryBuilder.TableName = DbSymbolMapper.identify[Ado.DbOptions.DbType].Insert(1, $"UnionTable{querys.Count}");
            for (int i = 0; i < querys.Count; i++)
            {
                queryBuilder.Union.Add(querys[i].ToString());
                queryBuilder.Parameters.Append(querys[i].QueryBuilder.Parameters);
            }
            queryBuilder.Columns = "*";
            return new QueryProvider<T>(Ado, queryBuilder);
        }

        /// <summary>
        /// 联合所有
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="querys">查询</param>
        /// <returns></returns>
        public IQuery<T> UnionAll<T>(params IQuery<T>[] querys) where T : class
        {
            return UnionAll(querys.ToList());
        }

        /// <summary>
        /// 联合所有
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="querys">查询</param>
        /// <returns></returns>
        /// <exception cref="ArgumentNullException"></exception>
        public IQuery<T> UnionAll<T>(List<IQuery<T>> querys) where T : class
        {
            if (querys == null || querys.Count == 0)
            {
                throw new ArgumentNullException(nameof(querys));
            }
            var queryBuilder = new QueryBuilder();
            queryBuilder.TableName = DbSymbolMapper.identify[Ado.DbOptions.DbType].Insert(1, $"UnionTable{querys.Count}");
            for (int i = 0; i < querys.Count; i++)
            {
                queryBuilder.UnionAll.Add(querys[i].ToString());
                queryBuilder.Parameters.Append(querys[i].QueryBuilder.Parameters);
            }
            queryBuilder.Columns = "*";
            return new QueryProvider<T>(Ado, queryBuilder);
        }
        #endregion

        #region 多租户实现
        /// <summary>
        /// 改变数据库
        /// </summary>
        /// <param name="dbId">数据库ID</param>
        public void ChangeDb(string dbId)
        {
            var options = dbOptionsList.FirstOrDefault(e => e.DbId == dbId);
            if (options == null)
            {
                throw new Exception($"DbId {dbId} 不存在");
            }
            if (!ados.ContainsKey(dbId))
            {
                ados.Add(dbId, DynamicProxyFactory.Create<IAdo>().Create(new AdoProvider(options), (method, args) =>
                {
                    Aop.DbLog?.Invoke(args[0] as string, args[1] as List<DbParameter>);
                }));
            }
            Ado = ados[dbId];
        }
        #endregion

        #region 释放

        /// <summary>
        /// 释放方法
        /// </summary>
        public void Dispose()
        {
            dbOptionsList.Clear();
            ados.Clear();
            GC.SuppressFinalize(this);
        }
        #endregion
    }
}
